在上一篇章我們學會提取點擊時的位置,本篇章也會用到相同的 function,我們先將他移出。
  const getClientOffset = (event: any) => {
    let rect = canvasRef.current.getBoundingClientRect();
    const point = {
      x: event.clientX - rect.left,
      y: event.clientY - rect.top,
    };
    return point;
  };
再來操作點擊line時紀錄位置
  /**
   * 滑鼠點下畫布
   */
  const handleMouseDown = (event: any) => {
    setIsDrawing(true);
    switch (tool) {
     ...
      case "line": // 畫直線
        const point = getClientOffset(event);
        lastPoint = { x: point?.x, y: point?.y };
        break;
      default:
        break;
    }
  };
再來在滑鼠移動時顯示直線的顯示位置
  const handleDrawCanvas = (point: { x: number; y: number }) => {
      const ctx = canvasRef.current.getContext("2d");
      switch (tool) {
      ....
        case "line": // 直線
          ctx.strokeStyle = activeColor;
          ctx.lineWidth = 1;
          ctx.beginPath();
          ctx.moveTo(lastPoint?.x, lastPoint?.y); // 下筆位置
          ctx.lineTo(point?.x, point?.y);
          ctx.stroke();
          break;
        default:
          break;
      }
    };
天R!這根本不是我們要的效果!觀察看看,的確是有畫上直線,但他在每個軌跡上都畫上了直線,所以我們需要在畫的過程中去清除軌跡。
在剛才畫線時,我們要操作清除多餘的線,讓畫面保持當前繪製的單一線
  const handleDrawCanvas = (point: { x: number; y: number }) => {
      const ctx = canvasRef.current.getContext("2d");
      switch (tool) {
      ....
        case "line": // 直線
        ....
          ctx.beginPath();
          ctx.moveTo(lastPoint?.x, lastPoint?.y);
          clearCanvas(); // 補上這行!!
          ctx.lineTo(point?.x, point?.y);
          ctx.stroke();
          break;
        default:
          break;
      }
    };
    
/** 清空畫布 **/
const clearCanvas = () => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext("2d");
    ctx.clearRect(0, 0, canvas.width, canvas.height);
};
看看效果
YA!!! 成功了~! 但會發現,因為每次的清空畫布,導致我們畫布上永遠只出現看到的那條線,結果本末倒置 XD...,但距離完成更近了,可以很直覺的想到:只需要儲存目前階段的畫布,再畫完線後,進行還原,是不是就可以解決這個問題呢?
帶上程式碼
  const [savedData, setSavedData] = useState<HTMLImageElement>(new Image());
  
   /** 儲存畫布 */
  const saveCanvas = () => {
    const canvas = canvasRef.current;
    const saved = new Image();
    saved.src = canvas.toDataURL("image/png");
    setSavedData(saved);
  };
  /** 還原畫布 */
  const restore = () => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext("2d");
    ctx.drawImage(savedData, 0, 0);
  };
看到 canvas 新的用法
toDataURL 可以將目前的 canvas 畫面轉為圖片,還可以指定圖片素質,甚至支援 webp格式
drawImage 則與toDataURL為相反,將圖片繪製上canvas,並且可指定大小及繪製位置。
有了上方兩個方法就可以有了上方兩個方法就可以在下筆時,進行儲存,在每次移動畫筆時,保持前一張畫布狀態,並清空多餘的軌跡線條。
 /**
   * 滑鼠點下畫布
   */
  const handleMouseDown = (event: any) => {
    setIsDrawing(true);
    switch (tool) {
      case "line":
        const point = getClientOffset(event);
        initialPoint = { x: point?.x, y: point?.y };
        saveCanvas(); // 加上儲存當前的畫布!!
        break;
      default:
        break;
    }
  };
  
  /** 滑鼠移動 **/
   const handleDrawCanvas = (point: { x: number; y: number }) => {
      ...
        case "line": // 直線
          clearCanvas(); //清空畫布
          restore(); //還原點擊時所儲存的畫布
          // draw the current line
          ctx.beginPath();
          ctx.moveTo(initialPoint?.x, initialPoint?.y);
          ctx.lineTo(point?.x, point?.y);
          ctx.stroke();
          break;
      ...
      }
    };
來看看效果吧

太棒了!順利完成!